有了前面的邏輯,就著使用Expression實作動態建立方法。
除了有能力動態建立方法,相比Emit有以下優點 :
可讀性好,可用熟悉的關鍵字,像是變數Variable對應Expression.Variable、建立物件New對應Expression.New 
方便Runtime Debug,可以在Debug模式下看到Expression對應邏輯代碼
 
所以特別適合介紹動態方法建立,但Expression相比Emit無法作一些細節操作,這點會在後面Emit講解到。
邏輯 :
User 動態方法(IDataReader reader)
{
	var user = new User();
	var value = reader[0];
	if( !(value is System.DBNull) )
		user.Name = (string)value;
	value = reader[1];
	if( !(value is System.DBNull) )
		user.Age = (int)value;	
	return user;
}
最後得出以下Exprssion版本代碼
public static class DemoExtension
{
	public static IEnumerable<T> Query<T>(this IDbConnection cnn, string sql) where T : new()
	{
		using (var command = cnn.CreateCommand())
		{
			command.CommandText = sql;
			using (var reader = command.ExecuteReader())
			{
				var func = CreateMappingFunction(reader, typeof(T));
				while (reader.Read())
				{
					var result = func(reader as DbDataReader);
					yield return result is T ? (T)result : default(T);
				}
			}
		}
	}
	private static Func<DbDataReader, object> CreateMappingFunction(IDataReader reader, Type type)
	{
		//1. 取得sql select所有欄位名稱
		var names = Enumerable.Range(0, reader.FieldCount).Select(index => reader.GetName(index)).ToArray();
		//2. 取得mapping類別的屬性資料 >  將index,sql欄位,class屬性資料做好對應封裝在一個變數內方便後面使用
		var props = type.GetProperties().ToList();
		var members = names.Select((columnName, index) =>
		{
			var property = props.Find(p => string.Equals(p.Name, columnName, StringComparison.Ordinal))
			?? props.Find(p => string.Equals(p.Name, columnName, StringComparison.OrdinalIgnoreCase));
			return new
			{
				index,
				columnName,
				property
			};
		});
		//3. 動態建立方法 : 從資料庫Reader按照順序讀取我們要的資料
		/*方法邏輯 : 
			User 動態方法(IDataReader reader)
			{
				var user = new User();
				var value = reader[0];
				if( !(value is System.DBNull) )
					user.Name = (string)value;
				value = reader[1];
				if( !(value is System.DBNull) )
					user.Age = (int)value;	
				return user;
			}
		*/
		var exBodys = new List<Expression>();
		{
			// 方法(IDataReader reader)
			var exParam = Expression.Parameter(typeof(DbDataReader), "reader");
			// Mapping類別 物件 = new Mapping類別();
			var exVar = Expression.Variable(type, "mappingObj");
			var exNew = Expression.New(type);
			{
				exBodys.Add(Expression.Assign(exVar, exNew));
			}
			// var value = defalut(object);
			var exValueVar = Expression.Variable(typeof(object), "value");
			{
				exBodys.Add(Expression.Assign(exValueVar, Expression.Constant(null)));
			}
			var getItemMethod = typeof(DbDataReader).GetMethods().Where(w => w.Name == "get_Item")
				.First(w => w.GetParameters().First().ParameterType == typeof(int));
			foreach (var m in members)
			{
				//reader[0]
				var exCall = Expression.Call(
					exParam, getItemMethod,
					Expression.Constant(m.index)
				);
				// value = reader[0];
				exBodys.Add(Expression.Assign(exValueVar, exCall));
				//user.Name = (string)value;
				var exProp = Expression.Property(exVar, m.property.Name);
				var exConvert = Expression.Convert(exValueVar, m.property.PropertyType); //(string)value
				var exPropAssign = Expression.Assign(exProp, exConvert);
				//if ( !(value is System.DBNull))
				//		(string)value
				var exIfThenElse = Expression.IfThen(
					Expression.Not(Expression.TypeIs(exValueVar, typeof(System.DBNull)))
					, exPropAssign
				);
				exBodys.Add(exIfThenElse);
			}
			// return user;	
			exBodys.Add(exVar);
			// Compiler Expression 
			var lambda = Expression.Lambda<Func<DbDataReader, object>>(
				Expression.Block(
					new[] { exVar, exValueVar },
					exBodys
				), exParam
			);
			return lambda.Compile();
		}
	}
}
查詢效果圖 :
最後查看Expression.Lambda > DebugView(注意是非公開屬性)驗證代碼 :
.Lambda #Lambda1<System.Func`2[System.Data.Common.DbDataReader,System.Object]>(System.Data.Common.DbDataReader $reader) {
    .Block(
        UserQuery+User $mappingObj,
        System.Object $value) {
        $mappingObj = .New UserQuery+User();
        $value = null;
        $value = .Call $reader.get_Item(0);
        .If (
            !($value .Is System.DBNull)
        ) {
            $mappingObj.Name = (System.String)$value
        } .Else {
            .Default(System.Void)
        };
        $value = .Call $reader.get_Item(1);
        .If (
            !($value .Is System.DBNull)
        ) {
            $mappingObj.Age = (System.Int32)$value
        } .Else {
            .Default(System.Void)
        };
        $mappingObj
    }
}
 -
-